#ifndef _game_Chat_hpp_
#define _game_Chat_hpp_

/** \file Chat.hpp
 *  \author Adrian Schweizer
 *  \created  $Sa 18 Aug 03:48:01 pm CEST 2007 schwadri@SchwadriComp.local$
 *  \modified $So 30 Sep 04:56:31 pm CEST 2007 schwadri@SchwadriLaptop$
 */

#include <boost/serialization/access.hpp>

#include "Common.hpp"
#include "../network/Types.hpp"
#include "../network/Client.hpp"

namespace game {

    /** \brief A container for Chat Messages
     *
     *  Allows only 2 modifying Operations:
     *  - Append new Chatmessage at the end of a Channel
     *  - Clear all messages
     */
    class Chat
    {
    public:
        static const int        LIMIT_CHAT_MESSAGE_SIZE =   256;    ///< the maximum allowed chat message text length (without trailing '\\0')

        /** \brief ctor*/
        Chat();

        /** \name Signal Types */ //@{
        //DEPRECATED:typedef boost::signal<void ()>          ChangedSignalType;  ///< the type of signal that is emitted whenever something has changed
        //@}

        /** \name Slot Types */ //@{
        //DEPRECATED:typedef ChangedSignalType::slot_type    ChangedSlotType;    ///< slot type corresponding to ChangedSignalType
        //@}

        /** \name Signals */ //@{
        /** \brief this signal is emitted whenever the internal state of the Chat object has changed
         *
         *  Whenever this happens all iterators to the internal message container are not guaranteed to be
         *  valid any longer
         */
        //DEPRECATED:mutable ChangedSignalType               ChangedSignal;
        //@}

        /** \brief Different Chat Channels*/
        enum ChannelType {
            //special channels
            CHANNEL_INVALID=-1,                 ///< does not exist
            CHANNEL_ALL=0,                      ///< all messages are also put into this channel regardless of the channel field
            CHANNEL_VALID_BEGIN=1,              ///< this is the first valid channel id that may be used
            CHANNEL_PUBLIC=CHANNEL_VALID_BEGIN, ///< normally all the messages go in here if not explicitly specified by the sender
            CHANNEL_SPECTATORS,                 ///< as soon as the game gets going all spectators send their messages to this channel
            CHANNEL_TEAM,
            CHANNEL_PRIVATE,
            CHANNEL_VALID_END=CHANNEL_PRIVATE,
        };

        /** \brief A Chat message
         */
        class Message
        {
        public:
            /** \brief ctor*/
            Message(network::Client::Id origin, char channel, network::Client::Id recipient,const std::string& text)
            :   m_origin(origin),
                m_channel(channel),
                m_recipient(recipient),
                m_text(text)
            { }

            /** \default ctor*/
            Message()
            :   m_origin(-1),
                m_channel(-1),
                m_recipient(-1)
            { }

            /** \name Accessors *///@{

            /** \brief get the clientid from which this message originated
             *
             *  \return If this message was sent by a regular client or the host the
             *  returned clientid corresponds to a valid \a clientid. If the message
             *  was not sent by anyone but is a status message then this returns \a -1
             */
            network::Client::Id   Origin() const      { return m_origin; }

            /** \brief returns the chat channel to which this message was sent
             *
             *  \see Chat::Channel
             */
            char                Channel() const     { return m_channel; }

            /** \brief get the chat message's recipient
             *
             *  Depending on which \a channel this message was sent the returned value
             *  has meaning or not. There is actually only 2 channels that need this value:
             *  -# \a TEAM      In this case the method returns the team for which this message is
             *  -# \a PRIVATE   The method returns the clientid of the client to whom this message was sent
             *  \see Message::Channel \see Chat::Channel
             */
            network::Client::Id   Recipient() const   { return m_recipient; }

            /** \brief the message text
             *
             *  \returns the unaltered message text as sent from its creator
             */
            const std::string&  Text() const        { return m_text; }
            //@}

            /** \name Mutators *///@{
            void    Origin(network::Client::Id o)     { m_origin=o; }

            void    Channel(char ch)                { m_channel=ch; }

            void    Recipient(network::Client::Id r)  { m_recipient = r; }

            void    Text(const std::string& text)   { m_text = text; }
            //@}

        private:
            network::Client::Id   m_origin;       ///< clientid of the message's creator
            char                  m_channel;      ///< channel to which this message was sent
            network::Client::Id   m_recipient;    ///< the message's recipient
            std::string           m_text;         ///< message text
            friend class boost::serialization::access;

            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_origin;
                    ar & m_channel;
                    ar & m_recipient;
                    ar & m_text;
                }
        };

    private:
        // typedef std::vector<Message>            message_container;  ///< type of the message container

        //typedef message_container::iterator     message_iterator;   ///< type of the message iterator
    public:

        class Channel
        {
        private:
            typedef std::vector<Message>                message_container;  ///< type of the message container
            typedef message_container::iterator         message_iterator;   ///< type of the message iterator
        public:
            typedef message_container::const_iterator   const_message_iterator; ///< message iterator type returned by message_begin() and message_end()

            /** \name Signal Types */ //@{
            typedef boost::signal<void ()>          ChangedSignalType;  ///< the type of signal that is emitted whenever a new message has been added
            //@}

            /** \name Slot Types */ //@{
            typedef ChangedSignalType::slot_type    ChangedSlotType;    ///< slot type corresponding to ChangedSignalType
            //@}

            /** \name Signals */ //@{
            /** \brief this signal is emitted whenever the internal state of the Channel object has changed
             *
             *  Whenever this happens all iterators to the internal message container are not guaranteed to be
             *  valid any longer
             */
            mutable ChangedSignalType               ChangedSignal;
            //@}

            /** \name Iterator methods *///@{

            /** \brief returns a message iterator which points at the beginning of the container
             *
             *  \note the iterator returned lets you iterate over all messages regardles of what channel
             *  they are in.
             */
            const_message_iterator  message_begin() const { return m_messages.begin(); }

            /** \brief returns a message iterator pointing one past the last message in the container
             *
             *  \see message_begin
             */
            const_message_iterator  message_end()   const { return m_messages.end(); }
            //@}

            /** \brief get the message at position \a idx
             *
             *  \warning \a idx must refer to a valid position (idx < size()) or the behaviour
             *  is undefined
             *  \return Const reference to the Message at position \a idx
             */
            const Message&          operator[](int idx) const { return m_messages[idx]; }

            /** \brief returns how many messages this channel contains*/
            std::size_t             size()              const { return m_messages.size(); }

            /** \brief Push a new message at the end of the Channel Message Container
             *
             *  \par Signals:
             *  Emits ChangedSignal upon successful completion.
             *  \param[in] m            Message
             *  \param[in] channel      channel
             *  \param[in] recipient
             */
            void PushBack(const Message& m);

            /** \brief destroy all chat messages and empty the internal container
             *
             *  \par Signals:
             *  ChangedSignal emitted after operation completion
             */
            void Clear();

        private:
            char                m_channel;
            message_container   m_messages; ///< this is where all the chat messages are kept
        };
    private:
        typedef std::vector<Channel*>    channel_container;
    public:
        typedef Channel*         channel_iterator;
        typedef const Channel*   const_channel_iterator;

        /** \name Iterator methods *///@{

        /** \brief returns a channel iterator which points at the beginning of the channel container
         *
         *  \see channel_end
         */
        channel_iterator        channel_begin()         { return m_channels; }
        const_channel_iterator  channel_begin() const   { return m_channels; }

        /** \brief returns a message iterator pointing one past the last message in the container
         *
         *  \see channel_begin
         */
        channel_iterator        channel_end()           { return m_channels+CHANNEL_VALID_END; }
        const_channel_iterator  channel_end()   const   { return m_channels+CHANNEL_VALID_END; }
        //@}

        /** \brief get a reference to the channel with id \a ch*/
        Channel&                operator[](char ch)     { return m_channels[ch]; }

        /** \brief Push a new message at the end of the Chat container
         *
         *  \param[in] m            Message
         *  \param[in] channel      This parameter is optional. If supplied it overrides the channel specified by \a m
         */
        void PushBack(const Message& m, char channel=CHANNEL_INVALID);

        /** \brief destroys all chat messages in all channels*/
        void Clear();

    private:
        //message_container   m_messages; ///< this is where all the chat messages are kept
        Channel     m_channels[CHANNEL_VALID_END];
    };
} // namespace game

#endif // _game_Chat_hpp_
